package lexer;

import java.io.*;
import java.util.*;
import symbols.*;

public class Lexer {
    public static int lineno = 1;
    char peek = ' ';		// must
    Hashtable<String, Word> words = new Hashtable<String, Word>();
    BufferedReader in;
    String line;
    int pos;
    boolean QUOTE_flag = false;
    boolean EOF_flag = false;
    void reserve(Word w) {
	words.put(w.lexeme, w);
    }
    public Lexer(File src) throws Exception {
	in = new BufferedReader(new FileReader(src));
	// line won't contain '\n'
	line = in.readLine();
	pos = 0;
	
	reserve(new Word("program", Tag.PROGRAM));
	reserve(new Word("procedure", Tag.PROCEDURE));
	reserve(new Word("begin", Tag.BEGIN));
	reserve(new Word("end", Tag.END));
	reserve(new Word("if", Tag.IF));
	reserve(new Word("else", Tag.ELSE));
	reserve(new Word("then", Tag.THEN));
	reserve(new Word("fi", Tag.FI));
	reserve(new Word("while", Tag.WHILE));
	reserve(new Word("do", Tag.DO));
	reserve(new Word("done", Tag.DONE));
	reserve(new Word("break", Tag.BREAK));
	reserve(new Word("type", Tag.TYPE));
	reserve(new Word("var", Tag.VAR));
	reserve(new Word("record", Tag.RECORD));
	reserve(new Word("array", Tag.ARRAY));
	reserve(new Word("of", Tag.OF));
	reserve(new Word("read", Tag.READ));
	reserve(new Word("write", Tag.WRITE));
	reserve(new Word("return", Tag.RETURN));
	reserve(Word.True); reserve(Word.False);
	reserve(Type.Int); reserve(Type.Char);
	reserve(Type.Bool);
    }
    void nextLine() throws Exception {
	line = in.readLine();
	lineno++;
	pos = 0;
    }
    void readch() throws Exception {
	if (line != null) {
	    if (pos < line.length()) {
		peek = line.charAt(pos);
		pos++;
	    }
	    else {
		nextLine();
		///		readch();
		peek = ' ';
	    }
	}
	else {
	    // EOF
	    EOF_flag = true;;
	    peek = ' ';
	}
    }
    boolean readch(char c) throws Exception {
	readch();
	if (peek != c)
	    return false;
	peek = ' ';
	return true;
    }
    public Token scan() throws Exception {
	for ( ; ; readch()) {
	    if (EOF_flag)
		// EOF
		return Token.Endfile;
	    if (peek == ' ' || peek == '\t')
		continue;
	    else if (peek == '{') {
		if (! QUOTE_flag) {
		    // In comment
		    do {
			readch();
			if (EOF_flag)
			    // EOF
			    return Token.Endfile;
		    } while (peek != '}');
		}
		else // Not comment
		    break;
	    }
	    else break;
	}
	if (peek == ':') {
	    if (readch('='))	// ASSIGN
		return new Token(Tag.ASSIGN);
	    else		// Something else
		return new Token(':');
	}
	if (EOF_flag)
	    // EOF
	    return Token.Endfile;
	if (Character.isDigit(peek)) {
	    int v = 0;
	    do {
		v = 10*v + Character.digit(peek, 10);
		readch();
	    } while (Character.isDigit(peek));
	    return new Num(v);
	}
	if (Character.isLetter(peek)) {
	    StringBuffer b = new StringBuffer();
	    do {
		b.append(peek);
		readch();
	    } while (Character.isLetterOrDigit(peek));
	    String s = b.toString();
	    Word w = (Word)words.get(s);
	    if (w != null) return w;
	    w = new Word(s, Tag.ID);
	    words.put(s, w);
	    return w;
	}
	if (peek == '\'') {
	    QUOTE_flag = ! QUOTE_flag;
	}
	Token tok = new Token(peek);
	peek = ' ';
	return tok;
    }
}
